home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1995 November / Macworld Nov ’95.toast / Developers / BoxMaker++ / BoxMaker++ ƒ / headers / boxmaker.h < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-14  |  19.9 KB  |  517 lines  |  [TEXT/KAHL]

  1. #pragma once
  2. //
  3. // This is a hack which is intended to make boxmaker compile under
  4. // both the old and the universal headers. It probably doesn't work since
  5. // (I don't have a _complete_ set of universal headers to check it with)
  6. //
  7. #if !NEW_HEADERS_AVAILABLE
  8.     typedef EventHandlerProcPtr AEEventHandlerUPP;
  9.     #define NewAEEventHandlerProc(entryPoint) (AEEventHandlerUPP)(entryPoint)
  10. #endif
  11. //
  12. // A shell's status will normally start at 'running', and then move either
  13. // to 'OApped' or 'finishing'. When a quit Apple event is received status
  14. // automatically moves to 'quitting'. The actual exit is only made when
  15. // 'EventloopHook' (a virtual function described below) returns true, and the
  16. // status is either 'finishing' or 'quitting'.
  17. //
  18. typedef enum shell_status
  19. {
  20.     shell_is_running,        // shell is running normally
  21.     shell_is_OApped,        // running normally, received OApp event
  22.     shell_is_finishing,        // started by ODOC, will quit on idle
  23.     shell_is_quitting        // Quit Apple Event received.
  24. };
  25.  
  26. class boxmaker : private boxmakergetfile
  27. {
  28.     public:
  29.         boxmaker( short dlogID = sfGetDialogID);
  30.         //
  31.         // A 'normal' main for a boxmaker is:
  32.         //
  33.         //        void main()
  34.         //        {
  35.         //            myboxmaker RedBand;
  36.         //            RedBand.run();
  37.         //        }
  38.         //
  39.         // the member function 'run' is needed because the 'run' can't be executed by
  40.         //
  41.         // - boxmaker's constructor because myboxmaker hasn't been constructed then
  42.         // - boxmaker's destructor because myboxmaker has already been destructed then
  43.         //
  44.         // Alternatively, myboxmaker could call 'run' at the end of its constructor,
  45.         // but there is no way to enforce that, so that method does not have any
  46.         // advantage over the one chosen here.
  47.         //
  48.         void run();
  49.  
  50.     protected:
  51.         //
  52.         // These are the actual routines to override.
  53.         //
  54.         // OpenDoc is the only one routine which _must_ be overridden.
  55.         // It should use either the information in the 'theCInfoPBRec' field or the
  56.         // information in the 'theFSSpec' field to determine the file to be processed.
  57.         // Note: OpenDoc is called before any of the 'mayEnterFolder', 'EnterFolder',
  58.         // 'ExitFolder', or 'CantEnterFolder' routines is called.
  59.         //
  60.         virtual void OpenDoc( Boolean opening) = 0L;
  61.         //
  62.         // mayEnterFolder should return a flag which indicates whether we should
  63.         // enter this particular folder. Normally you will make it to always return
  64.         // either 'true' or 'false', but you could also make it more complex.
  65.         // Thus, one can implement what Symantec calls 'shielded folders', or process
  66.         // all items of folders dropped upon the boxmaker, but not items in folders
  67.         // inside those folders. If 'mayEnterFolder' returns false, the folder is not
  68.         // entered; thus 'EnterFolder' nor 'ExitFolder' will be called for that folder.
  69.         //
  70.         virtual Boolean mayEnterFolder( Boolean opening) { return true;};
  71.         //
  72.         // EnterFolder and ExitFolder are called during a recursive descent.
  73.         // Under some circumstances you will receive both an OpenDoc and an
  74.         // EnterFolder message for folders encountered.
  75.         // The difference is that 'EnterFolder' is always called, and 'OpenDoc'
  76.         // is only called for a folder if the drop box can handle folders, as
  77.         // specified via the 'theFlags' parameter to its constructor.
  78.         //
  79.         virtual void EnterFolder( Boolean opening) {};
  80.         virtual void ExitFolder( Boolean opening) {};
  81.         //
  82.         // CantEnterFolder is called when we encounter a folder to which
  83.         // we do not have read access. In that case EnterFolder and ExitFolder
  84.         // are not called for the folder (if passFolders is set, OpenDoc will
  85.         // be called for the folder). Note: there are two different versions
  86.         // of read access, namely seeing files and seeing folders. You can
  87.         // use the 'getAccessRights' member to study the access rights.
  88.         // CantEnterFolder is called only when you have neither the see files
  89.         // nor the see folders access rights to a folder. For more complex
  90.         // selections you should override mayEnterFolder (see above)
  91.         //
  92.         virtual void CantEnterFolder( Boolean opening) {};
  93.         //
  94.         // OpenApp is called whenever the application receives an 'Open application'
  95.         // Apple Event. This happens whenever somebody opens the application without
  96.         // opening a document. Normally this is done by double-clicking the application,
  97.         // or by selecting 'Open' in the Finder's file menu after selecting the application.
  98.         // OpenApp is _not_ called when one drops files on the application, since the
  99.         // application will not receive an 'Open application' Apple event then.
  100.         // The default OpenApp simply shows the preferences dialog.
  101.         //
  102.         virtual void OpenApp() { ShowPreferences();};
  103.         //
  104.         // StartABunch and EndABunch are called at the start and end of the processing
  105.         // of an 'ODOC' event. The difference with 'OpenDoc' is that StartABunch/EndABunch
  106.         // are called exactly one time for every 'ODOC' Apple Event, not for every file
  107.         // or folder specified in that Apple Event. Also, the number of items specified
  108.         // does not include any items in subfolders, and it does not check whether the
  109.         // items are indeed processable by your application (e.g. when the 'BNDL' and 'typs'
  110.         // resources of your boxmaker do not match, or when the boxmaker is sent an
  111.         // Apple Event by AppleScript). The StartABunch/EndABunch combo can for instance
  112.         // be used to pop up a dialog requesting confirmation of a tricky operation, or
  113.         // to display and hide a progress box.
  114.         // The default versions of StartABunch and EndABunch are:
  115.         // StartABunch    : set the cursor to a watch cursor, disable all menus
  116.         // EndABunch    : reset the cursor to a NM arrow, enable all menus
  117.         //
  118.         virtual void StartABunch( long numTopLevelItems, Boolean opening);
  119.         virtual void EndABunch( long numTopLevelItems, Boolean opening);
  120.         //
  121.         // Routines to override only when one adds menus and/or menu items:
  122.         //
  123.         // If one changes the menus (boxmaker installs menu bar #128 and adds
  124.         // the desk accessories to menu #128) one should also override 'DoMenu'.
  125.         // To assist in implementing ones own 'DoMenu', the functions 'DoAppleMenu',
  126.         // 'SelectFile', 'ShowPreferences', and 'SendQuitToSelf' are provided.
  127.         //
  128.         virtual void DoMenu( long retVal);
  129.         //
  130.         // SelectFile by default lets one select _any_ file. Limiting the selection
  131.         // to certain file types can be done by adding a 'typs' resource #128 to
  132.         // the Application. For more complex selections override 'SelectFile'.
  133.         //
  134.         virtual void SelectFile();
  135.         //
  136.         // HandleDialogEvent is called whenever DialogSelect returns true.
  137.         // Normally one should just look at 'itemHit'; theDialog can be used to
  138.         // access the actual items, theEvent is passed because somebody may find
  139.         // a use for it.
  140.         // To change items from those specified in the resource (e.g. to change
  141.         // them to reflect the saved preferences), use the constructor of
  142.         // 'yourboxmaker'.
  143.         // The parameter 'theDialog' usually equals 'gMainDialog'. It is included
  144.         // for reasons of flexibility. This way, a boxmaker can display multiple
  145.         // dialogs, and determine which dialog a event is meant for.
  146.         //
  147.         virtual void HandleDialogEvent( short itemHit, DialogPtr theDialog) {};
  148.         //
  149.         // EventloopHook is called everytime through the main event loop. As all
  150.         // virtual members it can examine and change the 'myEvent' and 'gotEvent'
  151.         // fields.
  152.         // It can for instance be used in a boxmaker which does do a lengthy
  153.         // operation on files dropped on it. The 'OpenDoc' member function would
  154.         // then put the file or folder to be processed in a list, and 'EventloopHook'
  155.         // would take an item of the list, and process it partially. This way one can
  156.         // build a boxmaker which does call WaitNextEvent during processing of a file,
  157.         // and thus does not hog the Mac it is run on.
  158.         // (950116: Currently, the directory traversal routines do call WaitNextEvent
  159.         // every now and then. This was done since a lengthy traversal (e.g. of a
  160.         // one Gigabyte file server volume) already seriously hogs the Mac.
  161.         //
  162.         // EventloopHook is called directly after 'WaitNextEvent', before the event
  163.         // is processed. It is passed a Boolean which indicates whether the boxmaker
  164.         // itself would want to quit now (e.g. because the user chose 'Quit' in the
  165.         // file menu). It should return a boolean which indicates whether your boxmaker
  166.         // _can_ quit now. It is up to your boxmaker to decide what to do when its
  167.         // 'EventloopHook' is called with a value of true. Possible scenarios are
  168.         // (more or less in order of increased user-friendliness):
  169.         //
  170.         // - return true only when all file processing is done.
  171.         // - abort processing of files and return true.
  172.         // - return true and let your shell's destructor abort processing.
  173.         // - ask the user whether to abort or continue, and return false until
  174.         //   all processing is done.
  175.         // - ask the user whether to abort or continue, and return false until
  176.         //   all processing is done, and putting up a dialog explaining what is
  177.         //   going on, with a cancel button on it.
  178.         // When your EventloopHook does temporarily 'refuse' to quit when asked it
  179.         // probably is a good idea to let your opendoc handler return the value
  180.         // 'errAEEventNotHandled' after the quit request is received.
  181.         //
  182.         shell_status the_status;
  183.         virtual Boolean EventloopHook() { return true;};
  184.         //
  185.         // If your OpenDoc handler performs a lengthy operation you might want to call
  186.         // 'TimeForTea' every now and then in your OpenDoc handler.
  187.         // TimeForTea does not always call WaitNextEvent; if it doesn't call it
  188.         // it is fairly efficient, so you don't have to worry much about it slowing
  189.         // down your program.
  190.         //
  191.         void TimeForTea();
  192.         //
  193.         // the 'myEvent' field contains the last event obtained from 'WaitNextEvent'
  194.         // Thus it is available to all boxmaker's virtual member functions.
  195.         // gotEvent contains the value returned by the last call of WaitNextEvent.
  196.         // (this excludes the calls of WaitNextEvent done by TimeForTea);
  197.         //
  198.         EventRecord myEvent;
  199.         short gotEvent;
  200.         //
  201.         // These two fields contain the information about the current file
  202.         // whenever EnterFolder or OpenDoc is called. Both routines can use
  203.         // them for whatever they want to use them, as long as they do not change
  204.         // the fields ioVRefNum or ioNamePtr of 'theCInfoPBRec.hFileInfo'.
  205.         // A safer implementation of boxmaker would make these fields private, but
  206.         // then anybody wanting to use the information in a 'SetCatInfo' would have
  207.         // to allocate a CInfoPBRec and copy the information.
  208.         // When 'ExitFolder' is called the info in theCInfoPBRec does no longer
  209.         // point to the folder we are leaving.
  210.         //
  211.         // If 'theCInfoPBRec' points to a directory theSubDirID is set to the ID
  212.         // of that directory, so 'ExitFolder' may use this field to obtain the
  213.         // directory ID of the folder.
  214.         //
  215.         CInfoPBRec    theCInfoPBRec;
  216.         FSSpec        theFSSpec;
  217.         long        theSubDirID;
  218.         //
  219.         // 'Utility' functions which study theCInfoPBRec:
  220.         //
  221.         // getAccessRights assumes that theCInfoPBRec points to a directory,
  222.         // and returns the access rights, as documented in IM-V-391.
  223.         //
  224.         Boolean itsADirectory() const;
  225.         Boolean itsVisible() const;
  226.         
  227.         char getAccessRights() const;
  228.         //
  229.         // Miscellaneous utility functions:
  230.         //
  231.         static void copyStr63( const Str63 fromString, Str63 toString);
  232.         void SetUp_theFSSpec_from_theCInfoPBRec();
  233.         //
  234.         // itsProcessable checks whether 'theType' is member of the array 'theTypes',
  235.         // taking into account the situation where 'numTypes' equals -1.
  236.         // It also checks whether the file is invisible. itsProcessable can only
  237.         // be called for files. The array 'theTypes' is initialized from the 'typs'
  238.         // resource #128. This resource is supposed to contain the types of file the
  239.         // boxmaker is to process.
  240.         // This list can be restricted further by including a 'tycr' resource #128
  241.         // If it is present it should contain type-creator pairs to accept, with
  242.         // '****' a valid type or creator matching any type/creator.
  243.         //
  244.         Boolean itsProcessable() const;
  245.  
  246.         Boolean matchesTypeList() const;
  247.         Boolean matchesTypeCreatorPairs() const;
  248.         //
  249.         // ErrorAlert puts up an alert with a message as specified by the
  250.         // resource ID of a 'STR#' resource and an index into the string list.
  251.         // It uses 'ALRT' resource # kErrorAlertID, and returns to its caller.
  252.         // ErrorAlertQuit does not return, but does an 'ExitToShell'.
  253.         //
  254.         static void ErrorAlert( short errorNo,
  255.                     short stringIndexID, short stringListID = kErrStringID);
  256.         static void ErrorAlertQuit( short errorNo,
  257.                     short stringIndexID, short stringListID = kErrStringID);
  258.         
  259.         DialogPtr        gMainDialog;
  260.         DialogRecord    myDialogRecord;
  261.         //
  262.         // amInFront() returns true when 'we' are the current front procsess.
  263.         //
  264.         Boolean amInFront() const;
  265.         //
  266.         // 'SendQuitToSelf' does just that. It is the standard routine called when the
  267.         // quit menu item is chosen.
  268.         // Similarly, 'DoAppleMenu' is the standard Apple Menu routine, 'SelectFile' the
  269.         // standard 'Open…' item handler, and 'ShowPreferences' is the standard routine
  270.         // called for the 'Preferences…' menu item. They are made accessible to subclasses
  271.         // to make overriding 'DoMenu' easier. (SelectFile is listed above; it is virtual)
  272.         //
  273.         static void SendQuitToSelf();
  274.         void DoAppleMenu( short theItem);
  275.         void ShowAbout();
  276.         void ShowPreferences();
  277.         //
  278.         // errDebug invokes the Debugger when errno is unequal to noErr
  279.         // The second form not only prints the error number, but also the
  280.         // Pascal string specified.
  281.         //
  282.         static void errDebug( OSErr errno);
  283.         static void errDebug( OSErr errno, unsigned char *errString);
  284.         //
  285.         // our own address; used to send events to self. It is obtained once
  286.         // at initialization time, and stays around for the whole lifetime of
  287.         // the boxmaker.
  288.         //
  289.         static const AEAddressDesc self;
  290.  
  291.         ProcessSerialNumber myPSN;
  292.         //
  293.         // a Handle to the standard watch cursor:
  294.         //
  295.         static const CursHandle theClock;
  296.  
  297.     private:
  298.         //
  299.         // From now (940728) on most members are made protected, so that subclasses can
  300.         // get at them (e.g. to determine kFileMenuID when DoMenu is overridden, to study
  301.         // theTypes and numTypes in a version of 'SelectFile', to do a SendQuitToSelf, etc).
  302.         // We do keep some private routines and variables, though.
  303.         //
  304.         // The UPPs for the apple event handlers:
  305.         //
  306.         AEEventHandlerUPP OAPPPtr;
  307.         AEEventHandlerUPP ODOCPtr;
  308.         AEEventHandlerUPP PDOCPtr;
  309.         AEEventHandlerUPP QuitPtr;
  310.         //
  311.         // TickCount returns a signed -> time_of_next_cup_of_tea is also signed.
  312.         //
  313.         long time_of_next_cup_of_tea;
  314.         //
  315.         // Program routines
  316.         //
  317.         void InitToolbox();
  318.         void SetUpMenus();
  319.         Boolean InitGlobals();
  320.         void GetMainDialog();
  321.         void DoMouseDown();
  322.         void DoKeyDown();
  323.         //
  324.         // Apple Event stuff
  325.         //
  326.         void InitAEVTStuff();
  327.         static OSErr GotRequiredParams( AppleEvent *theAppleEvent);
  328.  
  329.         static AEAddressDesc GetTargetToSelf();        
  330.         static void _SendDocsToSelf( AEDescList *aliasList);
  331.         static void SendODOCToSelf( FSSpec *theFileSpec);
  332.  
  333.         OSErr _HandleDocs( AppleEvent *theAppleEvent,
  334.                                 AppleEvent *reply, Boolean opening);
  335.         //
  336.         // _HandleOneDoc is actually a misnomer; if the FSSpec happens to
  337.         // indicate a folder it may end up handling multiple files.
  338.         // Anyway, it is called repeatedly by _HandleDocs, once for every file opened.
  339.         //
  340.         OSErr _HandleOneDoc( const FSSpec &aFSSpec, Boolean opening);
  341.         
  342.         OSErr _HandleADir( long dirID, Boolean opening);
  343.  
  344.         void DoHighLevelEvent();
  345.         //
  346.         // GatherInfo gets info for the file specified in 'theCInfoPBRec' and the parameters
  347.         // passed to it. The first version collects info for the file specified, the second
  348.         // one collects info for a file in the directory specified. The volume reference
  349.         // number is supposed to be present in 'theCInfoPBRec', already.
  350.         //
  351.         OSErr GatherInfo( const FSSpec &aFSSpec);
  352.         OSErr GatherInfo( long theDirID, long index);
  353.         OSErr _GatherInfo( long theDirID);
  354.         //
  355.         // FailErr displays an error alert when 'err' is not zero,
  356.         // and always returns to its caller. It is for internal use.
  357.         //
  358.         static void FailErr( OSErr err);
  359.     //
  360.     // Apple event handles can not be member functions. Making them static should help
  361.     // since the C++ calling format equals that of Pascal, but I haven't gotten it to work.
  362.     // Therefore they are made friends.
  363.     //
  364.     friend pascal OSErr HandleOAPP( AppleEvent *theAppleEvent,
  365.                             AppleEvent *reply, long handlerRefcon);
  366.     
  367.     friend pascal OSErr HandleQuit( AppleEvent *theAppleEvent,
  368.                             AppleEvent *reply, long handlerRefcon);
  369.     
  370.     friend pascal OSErr HandleODOC( AppleEvent *theAppleEvent,
  371.                             AppleEvent *reply, long handlerRefcon);
  372.     
  373.     friend pascal OSErr HandlePDOC( AppleEvent *theAppleEvent,
  374.                             AppleEvent *reply, long handlerRefcon);
  375. };
  376.  
  377. pascal OSErr HandleOAPP( AppleEvent *theAppleEvent,
  378.                         AppleEvent *reply, long handlerRefcon);
  379.  
  380. pascal OSErr HandleQuit( AppleEvent *theAppleEvent,
  381.                         AppleEvent *reply, long handlerRefcon);
  382.  
  383. pascal OSErr HandleODOC( AppleEvent *theAppleEvent,
  384.                         AppleEvent *reply, long handlerRefcon);
  385.  
  386. pascal OSErr HandlePDOC( AppleEvent *theAppleEvent,
  387.                         AppleEvent *reply, long handlerRefcon);
  388.  
  389. inline void boxmaker::FailErr( OSErr error)
  390. {
  391.     if( error != noErr)
  392.     {
  393.         ErrorAlert( error, kAEVTErr);
  394.     }
  395. }
  396.  
  397. inline Boolean boxmaker::itsADirectory() const
  398. {
  399.     return (theCInfoPBRec.hFileInfo.ioFlAttrib & ioDirMask) != 0;
  400. }
  401.  
  402. inline Boolean boxmaker::itsVisible() const
  403. {
  404.     return (theCInfoPBRec.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible) == 0;
  405. }
  406.  
  407. inline char boxmaker::getAccessRights() const
  408. {
  409.     //
  410.     // for explanation on this hard-coded constant '31', see
  411.     // the comments in boxmaker.cp, function _GatherInfo
  412.     //
  413.     return ((char *)(&theCInfoPBRec))[ 31];
  414. }
  415.  
  416. inline void boxmaker::copyStr63( const Str63 fromString, Str63 toString)
  417. {
  418.     long *from = (long *)fromString;
  419.     long *to   = (long *)toString;
  420.     
  421.     for( int i = 0; i < 16; i++)
  422.     {
  423.         *to++ = *from++;
  424.     }
  425. }
  426.  
  427. inline OSErr boxmaker::GatherInfo( const FSSpec &aFSSpec)
  428. {
  429.     copyStr63( aFSSpec.name, theFSSpec.name);
  430.  
  431. //    theCInfoPBRec.hFileInfo.ioNamePtr    = theFSSpec.name;    done for us by boxmaker::boxmaker
  432. //    theCInfoPBRec.hFileInfo.ioVRefNum    = theVRef;            done for us by boxmaker::_HandleDocs
  433. //    theCInfoPBRec.hFileInfo.ioDirID        = theDirID;            done for us by _GatherInfo
  434.     theCInfoPBRec.hFileInfo.ioFDirIndex    = 0;
  435.  
  436.     return _GatherInfo( aFSSpec.parID);
  437. }
  438.  
  439. inline OSErr boxmaker::GatherInfo( long theDirID, long index)
  440. {
  441. //    theCInfoPBRec.hFileInfo.ioNamePtr    = theFSSpec.name;    done for us by boxmaker::boxmaker
  442. //    theCInfoPBRec.hFileInfo.ioVRefNum    = theVRef;            done for us by boxmaker::_HandleDocs
  443. //    theCInfoPBRec.hFileInfo.ioDirID        = theDirID;            done for us by _GatherInfo
  444.     theCInfoPBRec.hFileInfo.ioFDirIndex    = index;
  445.  
  446.     return _GatherInfo( theDirID);
  447. }
  448.  
  449. inline void boxmaker::errDebug( OSErr errno)
  450. {
  451.     if( errno != noErr)
  452.     {
  453.         Str15 errstring;
  454.         NumToString( errno, *(Str255 *)&errstring);
  455.         DebugStr( errstring);
  456.     }
  457. }
  458.  
  459. inline void boxmaker::SetUp_theFSSpec_from_theCInfoPBRec()
  460. {
  461.     theFSSpec.vRefNum = theCInfoPBRec.hFileInfo.ioVRefNum;
  462.     theFSSpec.parID   = theCInfoPBRec.hFileInfo.ioDirID;
  463. }
  464.  
  465. inline void boxmaker::ErrorAlertQuit( short errorNo,
  466.                     short stringIndexID, short stringListID)
  467. {
  468.     ErrorAlert( errorNo, stringIndexID, stringListID);
  469.     //
  470.     // SC++ patches ExitToShell, and calls all necessary desctructors
  471.     // (at least, I like to think it does)
  472.     //
  473.     ExitToShell();
  474. }
  475.  
  476. inline Boolean boxmaker::matchesTypeList() const
  477. {
  478.     return boxmakergetfile::matchesTypeList(
  479.                         theCInfoPBRec.hFileInfo.ioFlFndrInfo.fdType);
  480. }
  481.  
  482. inline Boolean boxmaker::matchesTypeCreatorPairs() const
  483. {
  484.     return boxmakergetfile::matchesTypeCreatorPairs(
  485.             theCInfoPBRec.hFileInfo.ioFlFndrInfo.fdType,
  486.             theCInfoPBRec.hFileInfo.ioFlFndrInfo.fdCreator);
  487. }
  488. //
  489. // These two structures are used to parse the Handle returned from GetMenuBar
  490. // (and that is done to disable or enable all menus)
  491. //
  492. // I'm not 100% sure about this. The structure might have changed under PPC, but
  493. // I think that that would be troublesome for mixed-mode programs
  494. //
  495. #if defined(powerc) || defined (__powerc)
  496.     #pragma options align=mac68k
  497. #endif
  498.  
  499. typedef struct menulistentry
  500. {
  501.     MenuHandle theMenu;
  502.     short  menuLeftEdge;
  503. };
  504.  
  505. typedef struct menulist
  506. {
  507.     short lastHandleOffset;    // = 6 * number of menus
  508.     short rightEdge;
  509.     short unused;
  510.     
  511.     struct menulistentry theMenus[ 1];    // actually kAnyNumber
  512. };
  513.  
  514. #if defined(powerc) || defined (__powerc)
  515.     #pragma options align=reset
  516. #endif
  517.